﻿using System.Collections.Generic;
using UnityEngine;

public class Border : MonoBehaviour
{
    public List<int> Vertices = new();
    public List<Vector3> VerticesPos = new();
    public Map.BorderClockWise BorderClockWise = Map.BorderClockWise.ClockWise;

    // Start is called before the first frame update
    private void Awake()
    {
        HunterAstarPath.Active.Border = new Vector3[Vertices.Count];
        for (var i = 0; i < Vertices.Count; i++)
        {
            HunterAstarPath.Active.Border[i] = VerticesPos[i];
        }
        HunterAstarPath.Active.BorderOrient = BorderClockWise;
    }

    // Update is called once per frame
    private void Update()
    {

    }

    private void OnDestroy()
    {
        HunterAstarPath.Active.Border = null;
    }

    private readonly Dictionary<int, Dictionary<int, int>> lineDict = new();
    private void AddLine(int i, int j)
    {
        var min = Mathf.Min(i, j);
        var max = Mathf.Max(i, j);
        if (lineDict.ContainsKey(min) == false)
        {
            lineDict[min] = new Dictionary<int, int>();
        }
        if (lineDict[min].ContainsKey(max) == false)
        {
            lineDict[min][max] = 0;
        }
        lineDict[min][max]++;
    }

    private GameObject mainGameObject;
    public void Refresh(GameObject main, Mesh mesh)
    {
        mainGameObject = main;
        lineDict.Clear();

        Vertices.Clear();
        VerticesPos.Clear();

        var realVerticesDic = new Dictionary<Vector3, int>();
        for (var i = 0; i < mesh.vertices.Length; ++i)
        {
            if (realVerticesDic.ContainsKey(mesh.vertices[i]) == false)
            {
                realVerticesDic[mesh.vertices[i]] = i;
            }
        }

        for (var k = 0; k < mesh.triangles.Length; k += 3)
        {
            var idxV0 = mesh.triangles[k];
            var idxV1 = mesh.triangles[k + 1];
            var idxV2 = mesh.triangles[k + 2];

            idxV0 = realVerticesDic[mesh.vertices[idxV0]];
            idxV1 = realVerticesDic[mesh.vertices[idxV1]];
            idxV2 = realVerticesDic[mesh.vertices[idxV2]];

            AddLine(idxV0, idxV1);
            AddLine(idxV1, idxV2);
            AddLine(idxV2, idxV0);
        }

        var lst = new List<int>();
        foreach (var pair1 in lineDict)
        {
            foreach (var pair2 in pair1.Value)
            {
                if (pair2.Value != 1)
                {
                    continue;
                }
                lst.Add(pair1.Key);
                lst.Add(pair2.Key);
            }
        }

        var dictLst = new List<Dictionary<int, int>> { new() };
        var dict = dictLst[0];
        var key = lst[0];
        var value = lst[1];
        dict[key] = value;
        lst.RemoveAt(1);
        lst.RemoveAt(0);

        while (lst.Count > 0)
        {
            var idx = lst.IndexOf(value);
            if (idx < 0)
            {
                key = lst[0];
                value = lst[1];
                dictLst.Add(new Dictionary<int, int>());
                dict = dictLst[^1];
                dict[key] = value;
                lst.RemoveAt(1);
                lst.RemoveAt(0);
                continue;
            }
            key = value;
            if (idx % 2 == 0)
            {
                value = lst[idx + 1];

                lst.RemoveAt(idx + 1);
                lst.RemoveAt(idx);
            }
            else
            {
                value = lst[idx - 1];

                lst.RemoveAt(idx);
                lst.RemoveAt(idx - 1);
            }

            dict[key] = value;
        }

        dictLst.Sort((a, b) => -a.Count.CompareTo(b.Count));

        var boarder = dictLst[0];

        foreach (var pair in boarder)
        {
            var pos1 = mesh.vertices[pair.Key];
            var pos2 = mesh.vertices[pair.Value];

            Vertices.Add(pair.Key);
            var pos3 = this.mainGameObject.transform.localToWorldMatrix.MultiplyPoint3x4(pos1);
            VerticesPos.Add(pos3);

            CreateCube(pair.Key.ToString(), pos1, pos2);
        }

        RefreshBorderClockWise();

#if UNITY_EDITOR
        UnityEditor.AssetDatabase.SaveAssets();
        UnityEditor.SceneManagement.EditorSceneManager.SaveOpenScenes();
#endif
    }


    public void Refresh()
    {
        //Vertices.Clear();
        //var child_cnt = transform.childCount;
        //for (int i = 0; i < child_cnt; i++)
        //{
        //    var child = transform.GetChild(i);
        //    Vertices.Add(int.Parse(child.name));
        //}

        //Utility.Trans.DestroyChildren(gameObject);

        //Vector3 pos1;
        //Vector3 pos2;
        //for (int i = 0; i < Vertices.Count - 1; i++)
        //{
        //    pos1 = VerticesPos[i];
        //    pos2 = VerticesPos[i + 1];

        //    CreateCube(Vertices[i].ToString(), pos1, pos2);
        //}

        //pos1 = VerticesPos[Vertices.Count - 1];
        //pos2 = VerticesPos[0];

        //CreateCube(Vertices[Vertices.Count - 1].ToString(), pos1, pos2);

        Core.Utils.Trans.RemoveAllChildren(gameObject);

        Vector3 pos1, pos2;
        for (var i = 0; i < Vertices.Count - 1; ++i)
        {
            pos1 = VerticesPos[i];
            pos2 = VerticesPos[i + 1];

            CreateObstacleBlock(Vertices[i].ToString(), pos1, pos2);
        }

        pos1 = VerticesPos[Vertices.Count - 1];
        pos2 = VerticesPos[0];
        CreateObstacleBlock(Vertices[^1].ToString(), pos1, pos2);

#if UNITY_EDITOR
        UnityEditor.AssetDatabase.SaveAssets();
        UnityEditor.SceneManagement.EditorSceneManager.SaveOpenScenes();
#endif
    }

    public void CreateCube(string name, Vector3 pos1, Vector3 pos2)
    {
        return;

        //GameObject go = new GameObject();
        //go.AddComponent<BoxCollider>();
        //go.name = name;
        //go.transform.parent = transform;
        //go.transform.position = (pos1 + pos2) / 2;
        //var dir = pos1 - pos2;
        //go.transform.forward = dir.normalized;

        //var local2world = m_main.transform.localToWorldMatrix;
        //var pos = go.transform.position;
        //pos = local2world.MultiplyPoint3x4(pos);
        //go.transform.position = pos;

        //go.transform.localScale = new Vector3(0.2f, 1f, dir.magnitude * 100);
    }
    private void CreateCube(string name, Vector3 pos)
    {
        var go = new GameObject();
        go.AddComponent<BoxCollider>();
        go.name = name;
        go.transform.parent = transform;
        go.transform.position = pos;
    }
    private void CreateObstacleBlock(string name, Vector3 pos1, Vector3 pos2)
    {
        Vector3 line = (pos2 - pos1);
        line.y = 0f;
        Vector3 normal = Vector3.Cross(line, Vector3.up);
        normal = normal.normalized;
        if (BorderClockWise == Map.BorderClockWise.CounterClockWise)
        {
            normal = -normal;
        }
        Vector3 midPos = (pos2 + pos1) / 2;
        Vector3 pos = midPos + normal;

        GameObject go = new GameObject();
        var comp = go.AddComponent<ObstacleBlock>();
        go.name = name;
        go.transform.parent = transform;
        go.transform.position = pos;
        var dir = pos1 - pos2;
        go.transform.forward = dir.normalized;
        comp.size = new Vector3(2, 1, dir.magnitude);
    }

    private void RefreshBorderClockWise()
    {
        Vector3 line = (VerticesPos[1] - VerticesPos[0]);
        line.y = 0f;

        Vector3 midPos = (VerticesPos[1] - VerticesPos[0]) / 2;
        for (int i = 0; i < VerticesPos.Count; ++i)
        {
            int next = (i + 1) % VerticesPos.Count;
            var candidate = (VerticesPos[next] - VerticesPos[i]);

            candidate.y = 0f;
            if (candidate.sqrMagnitude > 1)
            {
                line = candidate;
                midPos = (VerticesPos[i] + VerticesPos[next]) / 2;
                break;
            }
        }

        var normal = Vector3.Cross(line, Vector3.up);
        var testPos = midPos + 0.2f * normal.normalized;
        var border = new Vector3[VerticesPos.Count];
        for (int i = 0; i < VerticesPos.Count; ++i)
        {
            border[i] = VerticesPos[i];
        }
        if (Core.Utils.Math.IsInPolygon(border, testPos))
        {
            BorderClockWise = Map.BorderClockWise.CounterClockWise;
        }
        else
        {
            BorderClockWise = Map.BorderClockWise.ClockWise;
        }
    }

    public void Print()
    {
        Debug.Log(string.Join(",", Vertices));
        Debug.Log(string.Join(",", VerticesPos));

        for (int i = 0; i < VerticesPos.Count; ++i)
        {
            CreateCube(Vertices[i].ToString(), VerticesPos[i]);
        }
    }
}
